package mdp;

import java.util.Iterator;
import java.util.Vector;

import main.Main;
import env.City;
import env.Road;

public class Action
{

	public class Consequence
	{
		public State	state;
		public double	prob;

		public Consequence(State state, double prob)
		{
			this.state = state;
			this.prob = prob;
		}

		@Override
		public String toString()
		{
			return state.output() + "prob: " + prob + "\n";
		}

	}

	public State				actionState;	// the state of this action
	public City					from;
	public City					to;
	public Road					road;
	public boolean				chem;
	public boolean				army;
	public Double				actionCost;	// initial action cost, by road cost chem and army
	public Vector<Consequence>	consequences;

	public Action(State actionState, City from, City to, Road road,
			boolean chem, boolean army)
	{
		this.actionState = actionState;
		this.from = from;
		this.to = to;
		this.road = road;
		this.chem = chem;
		this.army = army;
		this.consequences = new Vector<Consequence>();
		this.actionCost = 0.0 + road.cost;
		if (chem)
			actionCost *= 2;
		if (army)
			actionCost *= 2;
	}

	public void connect()
	{
		// TODO no need to check finalState cause it will be checked in the invoker of this function.
		City chemCity = chem ? to : actionState.chem;
		City armyCity = army ? to : actionState.army;
		int[] roadsStates = actionState.world.getRoadsState();
		Iterator<Road> rIt = actionState.world.roads.iterator();
		int i = 0;
		int unknowRoads = 0;
		while (rIt.hasNext())
		{
			Road road = (Road) rIt.next();
			if (army && road == this.road)
				roadsStates[i] = 0;//clear road if moving with army
			if (roadsStates[i] == -1)
			{
				if (road.tChance == 1.0)
				{
					roadsStates[i] = 1;
				} else
				{
					if (road.tChance == 0.0)
						roadsStates[i] = 0;
					else
						unknowRoads++;
				}
			}
			i++;
		}
		if (unknowRoads != 0)
			multiConnect(unknowRoads, roadsStates, chemCity, armyCity);
		else
			singleConnect(roadsStates, chemCity, armyCity);
	}

	private void singleConnect(int[] roadsStates, City chemCity, City armyCity)
	{
		State s = Main.valueIteration.findState(to, chemCity, armyCity,
				roadsStates);
		s.reachable = true;
		s.generateActions();
		consequences.add(new Consequence(s, 1));
	}

	private void multiConnect(int unknownRoads, int[] roadsStates,
			City chemCity, City armyCity)
	{
		Vector<int[]> roadsStateVector = new Vector<int[]>();
		int i = 0;
		for (i = 0; i < (int) Math.pow(2, unknownRoads); i++)
		{
			roadsStateVector.add(roadsStates.clone());
		}
		int[] firstRoadsArray = roadsStateVector.elementAt(0);
		for (i = 0; i < firstRoadsArray.length; i++)
		{
			if (firstRoadsArray[i] == -1)
				firstRoadsArray[i] = 0;

		}
		for (i = 1; i < roadsStateVector.size(); i++)
		{
			int[] currRoadArr = roadsStateVector.elementAt(i);
			boolean changedToOne = false;
			for (int j = 0; j < currRoadArr.length; j++)
			{
				if (roadsStates[j] == -1)
				{
					if (!changedToOne)
					{
						if (roadsStateVector.elementAt(i - 1)[j] == 0)
						{
							currRoadArr[j] = 1;
							changedToOne = true;
						} else
						{
							currRoadArr[j] = 0;
						}
					} else
						currRoadArr[j] = roadsStateVector.elementAt(i - 1)[j];
				}
			}
		}

		for (i = 0; i < roadsStateVector.size(); i++)
		{
			double chance = 1;
			int[] currRoadArr = roadsStateVector.elementAt(i);
			for (int j = 0; j < currRoadArr.length; j++)
			{
				if (roadsStates[j] == -1)
				{
					if (currRoadArr[j] == 1)
						chance *= actionState.world.roads.elementAt(j).tChance;
					else
						chance *= (1 - actionState.world.roads.elementAt(j).tChance);
				}
			}
			State s = Main.valueIteration.findState(to, chemCity, armyCity,
					currRoadArr);
			s.reachable = true;
			s.generateActions();
			consequences.add(new Consequence(s, chance));
		}
	}

	public String outputConsequences()
	{
		String ans = "";
		Iterator<Consequence> cIt = consequences.iterator();
		while (cIt.hasNext())
		{
			Action.Consequence c = (Action.Consequence) cIt.next();
			ans += c.toString() + "\n";
		}
		return ans;
	}
	
	

	@Override
	public String toString()
	{
		String ans = "";
		Iterator<Consequence> cIt = consequences.iterator();
		while (cIt.hasNext())
		{
			Action.Consequence c = (Action.Consequence) cIt.next();
			ans += c.toString() + "\n";
		}
		return ans;
	}

	public Double calculateUtility()
	{
		Double ans = actionCost;
		Iterator<Consequence> cIt = consequences.iterator();
		while (cIt.hasNext())
		{
			Action.Consequence c = (Action.Consequence) cIt.next();
			ans += c.prob * c.state.utility;
		}
		return ans;
	}

	public String output()
	{
		return "best action: " + from.name + " -" + (chem ? "C" : "-")
				+ (army ? "A" : "-") + "-> " + to.name + "\n";
	}
}
